#include "draw.h"
#include "config.h"
#include "encoding_manager.h"
#include "display_manager.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "input_manager.h"
static unsigned char *fileMemStart;
static unsigned char *g_pucTextFileMemEnd;
static unsigned char *fileContentFirstPos;
static unsigned char *g_pucLcdNextPosAtFile;
static PT_EncodingOpr g_ptEncodingOprForFile;
static PT_InputEventOpr g_ptInputEventOprForFile;
static PT_InputEventOpr g_ptInputEventOprHead;
static PT_DispOpr g_ptDispOpr;
static int g_dwFontSize;

typedef struct PageDesc
{
    int iPage;
    unsigned char *pucLcdFirstPosAtFile;
    unsigned char *pucLcdNextPageFirstPosAtFile;
    struct PageDesc *ptPrePage;
    struct PageDesc *ptNextPage;
} T_PageDesc, *PT_PageDesc;
static PT_PageDesc g_ptCurPage = NULL;
static PT_PageDesc g_ptPages = NULL;

int OpenTextFile(char *pcFileName)
{
    struct stat fileStat;
    int ret;
    int fd = open(pcFileName, O_RDONLY);
    if (fd == -1)
    {
        printf("OpenTextFile's open function error \n ");
    }
    ret = stat(pcFileName, &fileStat);
    if (ret)
    {
        printf("OpenTextFile's stat function error\n ");
    }
    fileMemStart = mmap(NULL, fileStat.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (fileMemStart == (unsigned char *)-1)
    {
        printf("OpenTextFile's mmap function error\n ");
    }
    g_pucTextFileMemEnd = fileMemStart + fileStat.st_size;
    g_ptEncodingOprForFile = SelectEncodingOprForFile(fileMemStart);

    if (g_ptEncodingOprForFile)
    {
        fileContentFirstPos = fileMemStart + g_ptEncodingOprForFile->iHeadLen;
    }
    else
    {
        printf("OpenTextFile's SelectEncodingOprForFile function error\n ");
        return -1;
    }
    return 0;
}
int SetTextDetail(char *pcHZKFile, char *pcFileFreetype, unsigned int dwFontSize)
{
    PT_FontOpr ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
    while (ptFontOpr)
    {
        if (strcmp(ptFontOpr->name, "ascii") == 0)
        {
            ptFontOpr->FontInit(NULL, dwFontSize);
        }
        else if (strcmp(ptFontOpr->name, "gbk") == 0)
        {
            ptFontOpr->FontInit(pcHZKFile, dwFontSize);
        }
        else
        {
            ptFontOpr->FontInit(pcFileFreetype, dwFontSize);
        }
        ptFontOpr = ptFontOpr->ptNext;
    }
    g_dwFontSize = dwFontSize;
    return 0;
}
int SelectAndInitDisplay(char *pcName)
{
    g_ptDispOpr = GetDispOpr(pcName);
    if (g_ptDispOpr != NULL)
    {
        g_ptDispOpr->DeviceInit();
    }
    else
    {
        printf("SelectAndInitDisplay error \n");
        return -1;
    }
    return 0;
}
int ShowOneFont(PT_FontBitMap ptFontBitMap)
{
    int x, y;
    int i = 0;
    unsigned char ucByte;
    int bit;
    if (ptFontBitMap->iBpp == 1)
    {
        for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
        {
            i = (y - ptFontBitMap->iYTop) * ptFontBitMap->iPitch;
            for (x = ptFontBitMap->iXLeft, bit = 7; x < ptFontBitMap->iXMax; x++)
            {
                if (bit == 7)
                {
                    ucByte = ptFontBitMap->pucBuffer[i++];
                }

                if (ucByte & (1 << bit))
                {
                    g_ptDispOpr->ShowPixel(x, y, COLOR_FOREGROUND);
                }
                else
                {
                }
                bit--;
                if (bit == -1)
                {
                    bit = 7;
                }
            }
        }
        //   printf("ShowOneFont success \n");
        return 0;
    }
    else if (ptFontBitMap->iBpp == 8)
    {
        for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
            for (x = ptFontBitMap->iXLeft; x < ptFontBitMap->iXMax; x++)
            {
                if (ptFontBitMap->pucBuffer[i++])
                    g_ptDispOpr->ShowPixel(x, y, COLOR_FOREGROUND);
            }
        return 0;
    }
    else
    {
        printf("ShowOneFont ERROR \n");
        return -1;
    }
}
int IncLcdY(int y)
{
    if (y + g_dwFontSize < g_ptDispOpr->iYres)
        return (y + g_dwFontSize);
    else
        return 0;
}
int RelocateFontPos(PT_FontBitMap ptFontBitMap)
{
    int iLcdY;
    int iDeltaX;
    int iDeltaY;

    if (ptFontBitMap->iYMax > g_ptDispOpr->iYres)
    {
        return -1;
    }

    if (ptFontBitMap->iXMax > g_ptDispOpr->iXres)
    {

        iLcdY = IncLcdY(ptFontBitMap->iCurOriginY);
        if (0 == iLcdY)
        {
            return -1;
        }
        else
        {
            iDeltaX = 0 - ptFontBitMap->iCurOriginX;
            iDeltaY = iLcdY - ptFontBitMap->iCurOriginY;

            ptFontBitMap->iCurOriginX += iDeltaX;
            ptFontBitMap->iCurOriginY += iDeltaY;

            ptFontBitMap->iNextOriginX += iDeltaX;
            ptFontBitMap->iNextOriginY += iDeltaY;

            ptFontBitMap->iXLeft += iDeltaX;
            ptFontBitMap->iXMax += iDeltaX;

            ptFontBitMap->iYTop += iDeltaY;
            ptFontBitMap->iYMax += iDeltaY;
            ;

            return 0;
        }
    }

    return 0;
}
int ShowOnePage(unsigned char *pucTextFileMemCurPos)
{
    unsigned int dwCode;
    unsigned char *pucBufStart;
    int ret;
    T_FontBitMap tFontBitMap;
    PT_FontOpr ptFontOpr;
    int iOffset = 0;
    int bHasGetCode = 0;
    int bHasNotClrSceen = 1;
    pucBufStart = pucTextFileMemCurPos;
    tFontBitMap.iCurOriginX = 0;
    tFontBitMap.iCurOriginY = g_dwFontSize;
    while (1)
    {

        iOffset = g_ptEncodingOprForFile->GetCodeFrmBuf(pucBufStart, g_pucTextFileMemEnd, &dwCode);

        if (0 == iOffset)
        {
            /* 文件结束 */
            if (!bHasGetCode)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }

        bHasGetCode = 1;
        pucBufStart += iOffset;

        if (dwCode == '\n')
        {
            g_pucLcdNextPosAtFile = pucBufStart;
            tFontBitMap.iCurOriginX = 0;
            tFontBitMap.iCurOriginY = IncLcdY(tFontBitMap.iCurOriginY);
            if (0 == tFontBitMap.iCurOriginY)
            {

                return 0;
            }
            else
            {
                continue;
            }
        }
        else if (dwCode == '\r')
        {
            continue;
        }
        else if (dwCode == '\t')
        {
            dwCode = ' ';
        }

        ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;

        while (ptFontOpr != NULL)
        {

            ret = ptFontOpr->GetFontBitmap(dwCode, &tFontBitMap);
            if (ret == 0)
            {

                if (RelocateFontPos(&tFontBitMap))
                {

                    return 0;
                }

                if (bHasNotClrSceen)
                {

                    g_ptDispOpr->CleanScreen(COLOR_BACKGROUND);
                    bHasNotClrSceen = 0;
                }

                if (ShowOneFont(&tFontBitMap))
                {
                    return -1;
                }

                tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
                tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
                g_pucLcdNextPosAtFile = pucBufStart;

                break;
            }
            ptFontOpr = ptFontOpr->ptNext;
        }
    }
}

static void RecordPage(PT_PageDesc ptPageNew)
{
    PT_PageDesc ptPage;

    if (!g_ptPages)
    {
        g_ptPages = ptPageNew;
    }
    else
    {
        ptPage = g_ptPages;
        while (ptPage->ptNextPage)
        {
            ptPage = ptPage->ptNextPage;
        }
        ptPage->ptNextPage = ptPageNew;
        ptPageNew->ptPrePage = ptPage;
    }
}
int ShowNextPage(void)
{
    int ret;
    PT_PageDesc ptPage;
    unsigned char *pucTextFileMemCurPos;

    if (g_ptCurPage)
    {
        pucTextFileMemCurPos = g_ptCurPage->pucLcdNextPageFirstPosAtFile;
    }
    else
    {
        pucTextFileMemCurPos = fileContentFirstPos;
    }

    ret = ShowOnePage(pucTextFileMemCurPos);
    if (ret == 0)
    {
        if (g_ptCurPage && g_ptCurPage->ptNextPage)
        {
            g_ptCurPage = g_ptCurPage->ptNextPage;
            return 0;
        }
        ptPage = malloc(sizeof(T_PageDesc));
        if (ptPage)
        {
            ptPage->pucLcdFirstPosAtFile = pucTextFileMemCurPos;
            ptPage->pucLcdNextPageFirstPosAtFile = g_pucLcdNextPosAtFile;
            ptPage->ptPrePage = NULL;
            ptPage->ptNextPage = NULL;
            g_ptCurPage = ptPage;
            printf("%s %d, pos = 0x%x\n", __FUNCTION__, __LINE__, (unsigned int)ptPage->pucLcdFirstPosAtFile);
            RecordPage(ptPage);
            return 0;
        }
        else
        {
            return -1;
        }
    }

    return ret;
}

int ShowPrePage(void)
{
    int iError;

    printf("%s %d\n", __FUNCTION__, __LINE__);
    if (!g_ptCurPage || !g_ptCurPage->ptPrePage)
    {
        return -1;
    }

    printf("%s %d, pos = 0x%x\n", __FUNCTION__, __LINE__, (unsigned int)g_ptCurPage->ptPrePage->pucLcdFirstPosAtFile);
    iError = ShowOnePage(g_ptCurPage->ptPrePage->pucLcdFirstPosAtFile);
    if (iError == 0)
    {
        printf("%s %d\n", __FUNCTION__, __LINE__);
        g_ptCurPage = g_ptCurPage->ptPrePage;
    }
    return iError;
}

void *TaskRun(void *inputEventOpr)
{
    PT_InputEventOpr EventOpr = (PT_InputEventOpr)inputEventOpr;
    while (1)
    {
        EventOpr->GetInput((EventOpr->event));
        {
            if (*(EventOpr->event) == 'n')
            {
                ShowNextPage();
            }
            else if (*(EventOpr->event) == 'u')
            {
                ShowPrePage();
            }
            else
            {
                return NULL;
            }
        }
    }
    return NULL;
}

int TaskInit()
{
    pthread_t thread_id;
    g_ptInputEventOprHead = GetInputOprHead();
    PT_InputEventOpr tmp = g_ptInputEventOprHead;
    while (tmp)
    {
        int res = pthread_create(&thread_id, NULL, TaskRun, tmp);
        if (res != 0)
        {
            perror("Thread creation failed");
            exit(EXIT_FAILURE);
        }
        pthread_detach(thread_id);
        tmp = tmp->ptNext;
    }
    while(1);
    return 0;
}